Data Binding入门(一)

1.介绍

Google给Andorid开发者提供Data Binding技术,使用Data Binding库来写声明的layouts文件,并且用最少的代码来绑定你的app逻辑和layouts文件,同时使用Data Binding,我们可以很方便的实现 MVVM设计模式。

2.构建环境

要开始使用Data Binding,首先需要你的开发工具是Android Studio 1.3.0-beta1 或更高版本。

工作环境

新建一个 Project,确保 Android 的 Gradle 插件版本不低于 1.5.0-alpha1:

1
classpath 'com.android.tools.build:gradle:1.5.0'

然后修改对应模块(Module)的 build.gradle:

1
2
3
dataBinding {
enabled true
}

3.Data Binding Layout

Data Binding表达式

Data Binding layout文件有点不同的是:起始根标签是 layout,接下来一个 data 元素以及一个 view 的根元素。这个 view 元素就是你没有使用Data Binding的layout文件的根元素,使用 Data Binding 之后,Data Binding Layout就不再用于单纯地展示 UI 元素,还需要定义 UI 元素用到的变量举例说明如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="user" type="com.example.User"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.firstName}"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.lastName}"/>
</LinearLayout>
</layout>

在 data 内描述了一个名为user的变量属性,这个变量会为 UI 元素提供数据(例如 TextView 的 android:text),然后在 Java 代码中把『后台』数据与这个user的变量进行绑定:

1
2
3
<data>
<variable name="user" type="com.example.User"/>
</data>

其中 type 属性就是我们在 Java 文件中定义的 User 类。
当然,data 节点也支持 import,所以上面的代码可以换一种形式来写

1
2
3
4
<data>
<import type="com.example.User" />
<variable name="user" type="User" />
</data>

在layout的属性表达式写作@{},下面是一个TextView的text设置为user的firstName属性:

1
2
3
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.firstName}"/>

Data对象

添加一个 实体类 - User,非常简单,两个属性以及他们的 getter 和 setter。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class User {
private final String firstName;
private final String lastName;
public User(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
}

Binding数据

默认情况下,一个Binding类会基于layout文件的名称而产生,将其转换为Pascal case(译注:首字母大写的命名规范)并且添加“Binding”后缀。上述的layout文件是activity_main.xml,因此生成的类名是ActivityMainBinding。此类包含从layout属性到layout的Views中所有的bindings(例如user变量),并且它还知道如何给Binding表达式分配数值。创建bindings的最简单的方式是在inflating(layout文件与Activity/Fragment的“链接”)期间如下:

1
2
3
4
5
6
7
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
User user = new User("Test", "User");
binding.setUser(user);
}

运行app后,你将会看到Test User。

如果你在ListView或者RecyclerView adapter使用Data Binding时,你可能会使用:

1
ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);

4. 高级用法(一)

Import

零个或多个import元素可能在data元素中使用。这些只用在你的layout文件中添加引用,就像在Java中导入其他包一样:

1
2
3
<data>
<import type="android.view.View"/>
</data>

现在,View可以使用你的Binding表达式:

1
2
3
4
5
<TextView
android:text="@{user.lastName}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>

导入的类型可以在Variable和表达式中使用作为引用来使用。

注意:Android
Studio还没有处理imports,所以自动导入Variable在你的IDE不能使用。您的app仍会正常编译,你可以在您的Variable定义中使用完全符合规定的名称来解决该IDE问题。

类型别名

当类名有冲突时,其中一个类名可以用alias属性设置别名:

1
2
3
<import type="android.view.View"/>
<import type="com.example.widget.View"
alias="myView"/>

这样,在该layout文件中myView对应com.example.widget.View,而View对应android.view.View。

使用static属性和方法:

首先定义一个静态方法

1
2
3
4
5
6
7
8
public class MyStringUtils {
public static String capitalize(final String word) {
if (word.length() > 1) {
return String.valueOf(word.charAt(0)).toUpperCase() + word.substring(1);
}
return word;
}
}

然后在 xml 的 data 节点中导入:

1
2
3
4
5
6
7
8
9
<data>
<import type="com.example.MyStringUtils"/>
<variable name="user" type="com.example.User"/>
</data>
<TextView
android:text="@{MyStringUtils.capitalize(user.lastName)}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

就像在Java中,java.lang.*是自动导入的。

Variables

在data中可以使用任意数量的variable元素。每一个variable元素描述了一个用于layout文件中Binding表达式的属性。

1
2
3
4
5
6
<data>
<import type="android.graphics.drawable.Drawable"/>
<variable name="user" type="com.example.User"/>
<variable name="image" type="Drawable"/>
<variable name="note" type="String"/>
</data>

注意: 所有的 set 方法也是根据 variable 名称生成的。例如,上面我们定义了三个变量。 那么就会生成对应的三个 set 方法。

1
2
3
setUser(User user);
setImage(Drawable drawable);
setNote(String string)

该Variable类型在编译时检查,因此如果一个Variable实现了Observable或observable collection,这应该反映在类型中。(译注:需要查找资料来理解)如果variable是一个没有实现Observable接口的基本类或者接口,Variables不会被observed!

当对于多种配置有不同的layout文件时(如,横向或纵向),Variables会被合并。这些layout文件之间必须不能有冲突的Variable定义。

自定义 Binding 类名称

默认情况下,Binding类的命名是基于所述layout文件的名称,用大写开头,除去下划线以及(),第一个字母大写,然后添加“Binding”后缀。这个类将被放置在一个模块封装包里的databinding封装包下。例如,所述layout文件activity_main.xml将生成ActivityMainBinding。如果模块包是com.example.activity,那么它将被放置在com.example.activity.databinding。

Binding类可通过调整data元素中的class属性来重命名或放置在不同的包中,例如:

1
2
<data class="com.example.CustomBinding">
</data>

include

通过使用application namespace以及在属性中的Variable名字从容器layout中传递Variables到一个被包含的layout:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bind="http://schemas.android.com/apk/res-auto">
<data>
<variable name="user" type="com.example.User"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/name"
bind:user="@{user}"/>
<include layout="@layout/contact"
bind:user="@{user}"/>
</LinearLayout>
</layout>

注意:在name.xml以及contact.xml两个layout文件中必需要有user variable

如果在非根节点的 ViewGroup 中使用 include 会导致 crash,已经在 StackOverflow 上提了一个问题Android Data Binding makes app crash when using include tag in a non-root ViewGroup,直されたそうですけど。

使用资源数据

1
2
3
4
5
6
7
<TextView
android:padding="@{large? (int)@dimen/largePadding : (int)@dimen/smallPadding}"
android:background="@android:color/black"
android:textColor="@android:color/white"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />

表达式

常用表达式跟Java表达式很像,以下这些是一样的:

数学 + - / * %
字符串连接 +
逻辑 && ||
二进制 & | ^
一元运算 + - ! ~
移位 >> >>> <<
比较 == > < >= <=
instanceof
分组 ()
null
Cast
方法调用
数据访问 []
三元运算 ?:

示例:

1
2
3
android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}'

缺少的操作:

this
super
new
显式泛型调用

属性引用

当一个表达式引用一个类的属性,它用同样的格式对于字段、getters以及ObservableFields。

1
android:text="@{user.lastName}"

Null合并操作

1
android:text="@{user.displayName ?? user.lastName}"

就等价于

1
android:text="@{user.displayName != null ? user.displayName : user.lastName}"

避免 NullPointerException

Data Binding代码生成时自动检查是否为nulls来避免出现null pointer exceptions错误。例如,在表达式@{user.name}中,如果user是null,user.name会赋予它的默认值(null)。如果你引用user.age,age是int类型,那么它的默认值是0。

集合

常用的集合:arrays、lists、sparse lists以及maps,为了简便都可以使用[]来访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<data>
<import type="android.util.SparseArray"/>
<import type="java.util.Map"/>
<import type="java.util.List"/>
<variable name="list" type="List<String>"/>
<variable name="sparse" type="SparseArray<String>"/>
<variable name="map" type="Map<String, String>"/>
<variable name="index" type="int"/>
<variable name="key" type="String"/>
</data>
android:text="@{list[index]}"
android:text="@{sparse[index]}"
android:text="@{map[key]}"

字符串

当使用单引号包含属性值时,在表达式中使用双引号很容易:

1
android:text='@{map["firstName"]}'

使用双引号来包含属性值也是可以的。字符串前后需要使用”`”:

1
2
android:text="@{map[`firstName`]}"
android:text="@{map["firstName"]}"

热评文章